home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ For TASM / THUNK95.PAK / THUNK95.TXT < prev    next >
Text File  |  1996-02-21  |  13KB  |  240 lines

  1.                          Windows '95 and Flat Thunking
  2.  
  3.  
  4.  
  5. Contents
  6. ========
  7.  
  8.    Overview of Win32/Win32s thunking mechanisms
  9.    The flat thunking mechanism
  10.    A flat thunking example
  11.    Debugging notes
  12.    Recommended reading
  13.  
  14.  
  15.  
  16. Overview of Win32/Win32s thunking mechanisms
  17. ============================================
  18.  
  19. For the Win32s, Windows NT and Windows 95 platforms, Microsoft has provided
  20. us with three thunking mechanisms: universal thunking, general thunking, and
  21. flat thunking.
  22.  
  23. Universal thunking is available to Win32 applications running on Win32s, a
  24. 32-bit translation layer which allows 32-bit programs to be run on 16-bit
  25. Windows 3.x. Primarily, this mechanism accomodates 32-bit applications which
  26. are running in a hybrid environment of 32-bit modules inherent to the
  27. application itself and 16-bit modules inherent to the 16-bit environment.
  28.  
  29. General thunking is available on both Windows NT and Windows 95. Howevever,
  30. it is intended for use primarily on Windows NT and Microsoft documentation
  31. advises that applications using General Thunking should not be expected to
  32. have the same behaviour when run under Windows NT and Windows 95 due to
  33. differences in the operating systems. General thunking allows a 16-bit
  34. application to call into a 32-bit DLL, but not the reverse. Its usefulness
  35. seems to be primarily to support incremental porting of a 16-bit application
  36. to 32 bits, where the DLLs of the application would be ported first, and the
  37. executable only in a later revision, after all DLLs have been ported.
  38.  
  39. Flat thunking is available only under Windows 95. Flat thunking allows
  40. bi-directional calls between 16- and 32-bit modules. Using flat thunking
  41. requires that 16- and 32-bit modules be rebuilt, linking in special thunk
  42. modules created using the Microsoft thunk compiler and an assembler. Like
  43. general thunking, it seems primarily designed to support incremental porting
  44. of applications. An advantage of flat thunking is that you can call in
  45. either direction, and can even have a single call chain that moves back and
  46. forth between a pair of 16- and 32-bit modules. However, a module can only
  47. connect to one other module, so a bit of planning may be wise for larger
  48. projects.
  49.  
  50.  
  51. The flat thunking mechanism
  52. ===========================
  53.  
  54. Flat thunking requires that we create a special assembly module, assemble it
  55. twice to a 16-bit and a 32-bit object, then link those two objects into the
  56. corresponding 16- and 32-bit modules (DLLs or executables). The assembly
  57. file is created by first writing a thunk script, which is very much like a C
  58. header file containing function prototypes and type definitions. The
  59. Microsoft thunk compiler then translates the script into a single assembly
  60. source file containing both 16- and 32-bit code. Two assembler macros, IS_16
  61. and IS_32, allow the file to be assembled to the two distinct objects. Next,
  62. code is added to the 16- and 32-bit modules (DLL's or executables) to allow
  63. a runtime connection to be established between the two modules. Finally, a
  64. few modifications are made to the module definition files for each of the
  65. targets and the targets are brought up to date.
  66.  
  67. To examine the flat thunking mechanism, we will use an example of a 32-bit
  68. DLL calling a function in a 16-bit DLL, which is what the example "Thunker"
  69. does. In the function names referred to below, the "ThunkObj_" prefix is a
  70. result of names generated by the Microsoft thunk compiler which it derives
  71. from the filename of our thunk script. These are not standard names that you
  72. can look up in a help file.
  73.  
  74. First, the 32-bit DLL must establish a connection with the 16-bit DLL. This
  75. is done by calling ThunkObj_ThunkConnect32 and is most easily done from the
  76. 32-bit DLL's DllEntryPoint. This call causes the 16-bit DLL to be loaded. If
  77. the 16-bit DLL is marked for Windows 4.0, then the system will call its
  78. DllEntryPoint. A quirk is that the 16-bit DLL must have both a LIbMain and a
  79. DllEntryPoint. LibMain is called only when the DLL is loaded, while
  80. DllEntryPoint is called both on load and unload.
  81.  
  82. In DllEntryPoint, the 16-bit DLL establishes its side of the connection with
  83. the 32-bit DLL by calling ThunkObj_ThunkConnect16. The process of
  84. establishing this connection invokes code in the thunk modules which
  85. initializes jump tables for the exported functions. In the thunker example,
  86. the 16-bit DLL does not call into the 32-bit DLL, so the code generated for
  87. the 16-bit thunk module contains code only to initialize this jump table,
  88. after which, the 16-bit thunk module's job is done.
  89.  
  90. Calls can now be made between the 32- and 16-bit DLL's. Given that the
  91. 16-bit DLL exports a function Fun16, a call from the 32-bit DLL can be
  92. described. First, the call from the 32-bit DLL is actually resolved to a
  93. Fun16 procedure in the ThunkObj assembly module (which is why we never do
  94. anything to import Func16 from the 16-bit DLL). This thunk procedure fixes
  95. up the stack to account for a change from standard call to pascal calling
  96. convention, accomodates changes in data sizes (ie, size of int), and
  97. translates pointers (between 32-bit flat pointers expected in the 32-bit
  98. module and the selector:offset format expected in the 16-bit module). The
  99. thunk then uses the jump table to call directly into the 16-bit DLL.
  100.  
  101. The connection is broken by calling the same ThunkObj_ThunkConnect32 and
  102. ThunkObj_ThunkConnect16 functions, but passing DLL_PROCESS_DETACH as the
  103. last parameter.
  104.  
  105.  
  106. A flat thunking example
  107. =======================
  108.  
  109. The example, Thunker, provides a shell demonstration of flat thunking. In
  110. this example, a 32-bit application uses a 32-bit DLL which calls into a
  111. 16-bit DLL. Microsoft documentation recommends that we thunk between DLL's
  112. rather than directly from an executable to a DLL as we can later port the
  113. 16-bit DLL and then simply replace the 16- and 32-bit thunking DLL's with
  114. one new 32-bit DLL. Since flat thunking has the limitation of a module being
  115. able to connect to only one other module, it is the only way to have a
  116. 32-bit executable effectively use multiple 16-bit DLL's, or vice-versa.
  117. Also, the model of interfacing thru a DLL is conducive to a multi-platform
  118. application which will determine at runtime which interface DLL to use
  119. (since no one thunking mechanism will work on all Windows 32-bit platforms).
  120.  
  121. The example demonstrates all the basic requirements for coding and building
  122. an application using flat thunking. I strongly recommend you read thru the
  123. makefile and all the associated source - including the module definition
  124. files. Omitting a step or requirement can lead to strange runtime failures
  125. that can be difficult to debug.
  126.  
  127. Further information on flat thunking is available in the Win32 online help
  128. file. Go to index item Thunk Compiler and page thru the "chapter". Further
  129. information on general and universal thunking is available in the Microsoft
  130. Win32 SDK documentation, and on the Microsoft Developer's Network.
  131.  
  132. This example demonstrates calling from a 32-bit application into a 16-bit
  133. DLL. Per recommendations in Microsoft technical documentation, this is done
  134. by calling thru a 32-bit DLL. The example presumes that all calls into the
  135. 16-bit DLL are made only from the 32-bit DLL and describes modifications
  136. from that starting point. The application model looks like this:
  137.  
  138.    ┌────────┐    ┌────────┐              ┌─────────┐
  139.    │ 32.exe │───>│ 32.dll │              │ 16.dll  │
  140.    └────────┘    │  ┌─────┴────┐     ┌───┴──────┐  │
  141.                  └──┤ to32.obj │────>│ to16.obj ├──┘
  142.                     └──────────┘     └──────────┘
  143.  
  144. At runtime, the executable causes the 32-bit DLL to be loaded and that DLL
  145. establishes a connection by calling a Thunk_Connect function exported from
  146. the 16-bit thunk object. The 16-bit side then initializes a jump table which
  147. is passed back to the 32-bit DLL. When a call is made to a function exported
  148. from the 16-bit DLL, the call from the 32-bit DLL call actually resolves to
  149. a function in the 32-bit thunk object. This function then performs the steps
  150. neccassary to translate the call (ie, allowing for different sizes in data
  151. types) and then calls thru the jump table to the actual function in the
  152. 16-bit DLL. On return, control returns to the thunk function in the 32-bit
  153. thunk object, which does some translating of return values and structures
  154. passed by reference and ultimately returns to the original caller.
  155.  
  156. To implement a thunking application, you must:
  157.  
  158.    1) Create a thunk script providing declarative information about
  159.       functions and data structures being thunked.
  160.  
  161.    2) Generate assembly source from the thunk script using the Microsoft
  162.       thunk compiler.
  163.  
  164.    3) Assemble the thunk module to a 16-bit object module which will be
  165.       linked into the 16-bit DLL exporting the user functions.
  166.  
  167.    4) Assemble the thunk module to a 32-bit object module which will be
  168.       linked into the 32-bit DLL that will be importing the user functions.
  169.  
  170.    5) Modify the 16-bit DLL to provide a DllEntryPoint function which will
  171.       be called to establish a connection with the 32-bit DLL.
  172.  
  173.    6) Modify the 16-bit DLL's module definition file to:
  174.          a) Add SUBSYSTEM 4.0 so the DLL is marked for Windows 4.0;
  175.          b) Export DllEntryPoint, the 16-bit thunk data, and those functions
  176.          you wish to call from the 32-bit world; and,
  177.          c) Import C16THKSL01 and THUNKCONNECT16 from kernel.
  178.  
  179.    7) Modify the 32-bit DLL to establish and break the connection with the
  180.       16-bit DLL at runtime.
  181.  
  182.    8) Modify the 32-bit DLL's module definition file to:
  183.          a) import ThunkConnect32@24 from kernel32
  184.          b) export the 32-bit thunk data.
  185.  
  186. Special notes:
  187.  
  188. The 16-bit DLL must be marked for subsystem 4.0, else the added
  189. DllEntryPoint function will not be called to establish the connection. This
  190. can be done with the linker /V switch or with a SUBSYSTEM entry in the
  191. module definition file. Of course, having done this, the DLL will only work
  192. on Windows 95.
  193.  
  194. When assembling the thunk module, you must use the assembler's /ml option
  195. with /DIS_32 and must not use it with /DIS_16. This is because the thunk
  196. compiler always generates case sensitive symbols. By default, the 32-bit
  197. side of the thunk mechanism uses standard call calling convention, for which
  198. the case sensitivity must be preserved. The 16-bit side of the thunk
  199. mechanism uses pascal calling convention and the symbols there should be
  200. forced to upper case. (Since the thunk compiler generates functions which
  201. manipulate parameters being passed between the 16- and 32-bit modules, do
  202. not attempt to override the default calling conventions.)
  203.  
  204. You may notice that in the 32-bit thunk module, the generated names for
  205. functions are created by adding to the function name an at sign ('@')
  206. followed by a numeric value representing the width of the parameter list.
  207. This is the naming convention that Microsoft uses for stdcall calling
  208. convention and the names are generated by the Microsoft thunk compiler. To
  209. resolve them, Turbo Assembler, when given the -utthk switch, automatically
  210. generates aliases which match the Borland naming convention. You can examine
  211. both forms of these names using tdump with this syntax:
  212.  
  213.    tdump -oipubdef -oiextdef -oialias thkobj16.obj
  214.    tdump -oipubdef -oiextdef -oialias thkobj32.obj
  215.  
  216. The 32-bit module can be built with debug info, but the 16-bit module cannot
  217. as tasm32 generates only 32-bit debug info which the 16-bit linker does not
  218. expect. Also, the -utthk switch is unique to tasm32 and is rejected by tasm
  219. and tasmx.
  220.  
  221.  
  222. Debugging notes
  223. ===============
  224.  
  225. Turbo Debugger for Win32 cannot step from 32-bit code into 16-bit code. It
  226. is possible to step thru the 32-bit thunk module in source assembly or in
  227. the debuggers CPU view. While in the CPU view, do not attempt to step into a
  228. call thru the thunk table into the 16-bit DLL as it will cause TD32 to hang.
  229. Attempting to debug with available tool sets at this level will likely be
  230. very challenging and require very good Assembly language skills.
  231.  
  232.  
  233. Recommended reading
  234. ===================
  235. Additional information on thunking under various Microsoft Windows'
  236. environments can be found on the Microsoft Developers Network CD and in the
  237. Microsoft Win32 SDK documentation. Of particular interst are the articles
  238. "Flat Thunking Mechanics" and "How to Debug Flat Thunks" on the MSDN CD.
  239.  
  240.